// filename **********OS.H***********
// Real Time Operating System for Labs 2 and 3 
// Jonathan W. Valvano 2/3/11, valvano@mail.utexas.edu
//***********Ready to go*************
// You may use, edit, run or distribute this file 
// You are free to change the syntax/organization of this file
 
#ifndef __OS_H
#define __OS_H

#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/timer.h"
#include "driverlib/interrupt.h"

#include "driverlib/sysctl.h"
#include "driverlib/gpio.h"
#include "LM3Sxxxx.H"

extern OSStartHighRdy(void);
extern void InitConsole(void);

#define STACK_SIZE 128

// fill these depending on your clock        
#define TIME_1MS  50000          
#define TIME_2MS  2*TIME_1MS

//Priority definitions
enum SCHEDULER_PRIORITIES{
	HIGHEST_PRIORITY = 0,
	HIGH_PRIORITY,
	NORMAL_PRIORITY,
	LOW_PRIORITY,
	LOWEST_PRIORITY,
	MAX_NUM_PRIORITIES
};
#define BLOCKED	MAX_NUM_PRIORITIES

/*
*********************************************************************************************************
*                                              DATA TYPES
*                                         (Compiler Specific)
*********************************************************************************************************
*/

typedef unsigned char  bool_t;
typedef unsigned char  uint8_t;                    /* Unsigned  8 bit quantity                           */
typedef signed   char  int8_t;                    /* Signed    8 bit quantity                           */
typedef unsigned short uint16_t;                   /* Unsigned 16 bit quantity                           */
typedef signed   short int16_t;                   /* Signed   16 bit quantity                           */
typedef unsigned int   uint32_t;                   /* Unsigned 32 bit quantity                           */
typedef signed   int   int32_t;                   /* Signed   32 bit quantity                           */
typedef float          fp32_t;                     /* Single precision floating point                    */
typedef double         fp64_t;                     /* Double precision floating point                    */

typedef unsigned int   OS_STK;                   /* Each stack entry is 32-bit wide                    */

// feel free to change the type of semaphore, there are lots of good solutions
struct  Sema4{
  int Value;   // == 0 means free, otherwise means busy
  int MaxNumThreads; //not used for binary semaphores
  int ownerID;
  int lastOwner; 
};
typedef struct Sema4 Sema4Type;


/*
*********************************************************************************************************
*                                          TASK CONTROL BLOCK
*********************************************************************************************************
*/
typedef struct os_tcb {
	 OS_STK * SP;
	 OS_STK stack[STACK_SIZE];
	 struct os_tcb *next, *prev;
	 uint32_t id;
	 uint32_t sleep_ctr;
	 uint32_t priority;
	 uint32_t oldPriority;
	 Sema4Type *sema4BlockPt;
	 tBoolean deleted; 
} OS_TCB;


//********************************OS_Init**************************************
//	Initializes the operating system. Sets up peripherals. Initializes systick.
//! \param None
//! \return None
//*****************************************************************************
void OS_Init(void); 

//****************************OS_InitSemaphore**********************************
//	Initializes a semaphore.
//! \param semaPt Pointer to the semaphore to initialize
//! \param value Value to initialize the semaphore to. This paramater should
//!	almost always be 0.
//!	\param maxNumThreads For counting semaphores, this should be a number
//!	greater than 1. For binary semaphores, this should equal 1.
//! \return None
//! TODO Eliminate the value field. It is not really used
//*****************************************************************************
void OS_InitSemaphore(Sema4Type *semaPt, long value, long maxNumThreads); 

//*********************************OS_Wait**************************************
//	Spin / block on a counting semaphore.
//! \param semaPt Pointer to the semaphore to wait on.
//! \return None
//*****************************************************************************
void OS_Wait(OS_TCB *thread, Sema4Type *semaPt); 

//********************************OS_Signal************************************
//	Release a counting semaphore.
//! \param semaPt Pointer to the semaphore to release
//! \return None
//*****************************************************************************
void OS_Signal(OS_TCB *thread, Sema4Type *semaPt); 

//*********************************OS_bWait************************************
//	Spin / blocks on a binary semaphore
//! \param *thread Pointer to the thread that is calling this fucntion
//! \param semaPt Pointer to the semaphore to wain on.
//! \return None
//*****************************************************************************
void OS_bWait(OS_TCB *thread, Sema4Type *semaPt); 

//*******************************OS_bSignal************************************
//	Releases a binary semaphore
//! \param *thread Pointer to the thread that is calling this fucntion
//! \param semaPt Pointer to the semaphore to release
//! \return None
//*****************************************************************************
void OS_bSignal(OS_TCB *thread, Sema4Type *semaPt); 

//******************************OS_AddThread***********************************
//	Adds a thread to the scheduler linked list. Thread TCBs are initialized
//	here before being added to the list.
//! \param task Entry point for the thread
//!	\param stackSize TCB stack size for the thread. This is currently ignored.
//!	\param priority	The priority of the thread. This field is currently ignored.
//! \return 1 on success, 0 otherwise
//!	TODO! Implement priority and stacksize
//*****************************************************************************
int OS_AddThread(void(*task)(OS_TCB* this), 
   unsigned long stackSize, unsigned int priority);

//****************************OS_SetPriority***********************************
//	Changes the priority of a thread.
//! \param *thread Pointer to the thead whose priority is being changed.
//! \param newPriority New priority level this thread should execute at
//! \return 1 on Success, 0 otherwise
//! WARNING! This function should only be called with interrupts disabled!
//*****************************************************************************
int OS_SetPriority(OS_TCB *thread, uint32_t newPriority);

//***********************************OS_Id*************************************
//	Returns the thread ID for the currently running thread	
//! \param None
//! \return ID of currently running thread
//*****************************************************************************
unsigned long OS_Id(void);

//***************************OS_AddPeriodicThread*******************************
//	Adds a background periodic task.
//! \param task Entry point for the thread
//!	\param period period given in ms
//!	\param priority	The priority of the thread. This field is currently ignored.
//! \return 1 on success, 0 otherwise
//!	TODO! Implement priority
//*****************************************************************************
int OS_AddPeriodicThread(void(*task)(void), unsigned long period, unsigned long priority);

//*****************************OS_AddButtonTask********************************
//	Adds a background thread to run whenever the up button is pressed on the
//	board.
//! \param task Entry point for the thread
//!	\param priority	The priority of the thread. This field is currently ignored.
//! \return 1 on success, 0 otherwise
//!	TODO! Implement priority
//*****************************************************************************
int OS_AddButtonTask(void(*task)(void), unsigned long priority);

int OS_AddButtonSelectTask(void(*task)(void), unsigned long priority);

//**********************************OS_Sleep***********************************
//	Suspends the current thread for the given number of ms. Context switch 
//	is triggered
//! \param *thread Pointer to the thread that is calling this fucntion
//! \return None
//	NOTE! Be careful! PendSV may not trigger immediately! Upto 12 clock cycles
//	can pass before PendSV is triggered
//*****************************************************************************
void OS_Sleep(OS_TCB* thread, unsigned long sleepTime); 

//***********************************OS_Kill************************************
//	Removes a thread from the scheduler list and deallocates its memory. This is
//	called when a thread dies
//! \param current Current TCB
//! \return None
//	Note! Should be called exactly once every ms
//!	TODO! There is a bug when deallocating memory and removing from the 
//!	scheduler list. A bug fix is in progress but not complete yet. For now 
//! mark threads as deleted (blocked) to prevent them from running.
//*****************************************************************************
void OS_Kill(void);

//*******************************OS_ForceKill***********************************
//	Removes a thread from the scheduler list and deallocates its memory. This is
//	used to kill a third party thread
//! \param thread Pointer to thread that should be killed
//! \return None
//*****************************************************************************
void OS_ForceKill(OS_TCB* thread);

//*********************************OS_Suspend**********************************
//	Suspends the current thread and triggers a context switch
//! \param None
//! \return None
//	NOTE! Be careful! PendSV may not trigger immediately! Upto 12 clock cycles
//	can pass before PendSV is triggered
//*****************************************************************************
void OS_Suspend(void);

//*******************************OS_MailBox_Init*******************************
//	Initialize mailbox communication channel. Data cannot be placed in a mail-
//	box until it has been read.
//! \param None
//	\return None
//*****************************************************************************
void OS_MailBox_Init(void);

//*******************************OS_MailBox_Send*******************************
//	Put data in the mailbox to be read by another thread
//!	\param *thread Pointer to the thread that is calling this fucntion
//! \param data	4 bytes of data
//	\return None
//	NOTE! This fucntion will spin/block if the MailBox contains data not yet
//	received
//***************************************************************************** 
void OS_MailBox_Send(OS_TCB* thread, unsigned long data);

//*******************************OS_MailBox_Recv*******************************
//	Recieve data from the mailbox
//! \param *thread Pointer to the thread that is calling this fucntion
//	\return Data recieved from the mailbox
//	NOTE! This fucntion will spin/block if the MailBox is empty
//***************************************************************************** 
unsigned long OS_MailBox_Recv(OS_TCB *thread);

//***********************************OS_Time***********************************
//	Returns the time in 20ns units
//! \param None
//	\return System time in 20ns units
//	NOTE! System time can roll over
//*****************************************************************************
unsigned long long OS_Time(void);

//***********************************OS_Time***********************************
//	Returns the time in 1ms units
//! \param None
//	\return System time in 20ns units
//	NOTE! System time can roll over
//*****************************************************************************
unsigned long long OS_MSTime(void);


///*******************************OS_TimeDifference*****************************
//	Calculates difference between two times
//! \param stop End time
//!	\param start Start time
//	\return Time difference in 20ns units
//*****************************************************************************
long long OS_TimeDifference(unsigned long long start, unsigned long long stop);

//********************************OS_ClearMsTime*******************************
//	Sets the system time to 0
//! \param None
//	\return None
//*****************************************************************************
void OS_ClearMsTime(void);

//**********************************OS_MsTime**********************************
//	Reads the current time in msec
//! \param None
//	\return Time in ms
//! TODO! This function is not currently implemented
//*****************************************************************************
unsigned long OS_MsTime(void);

//*********************************SystemTime**********************************
//	Used to increment system ms time.
//! \param None
//	\return None
//! TODO! This function is not currently implemented
//*****************************************************************************
void SystemTime(void);

//**********************************OS_Launch**********************************
//	Launches the operating system. OS_Init() should be called prior to this
//! \param theTimeSlice Time slice for each thread. Not currently implemented
//	\return None
//!	TODO! Implement thread time slices
//*****************************************************************************
void OS_Launch(unsigned long theTimeSlice);

#endif
